package com.abo.yaas.webservice;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriBuilderException;

import com.abo.yaas.authentication.SessionHandler;
import com.abo.yaas.authentication.UserChangeEvent;
import com.abo.yaas.authentication.UserChangeListener;
import com.abo.yaas.domain.Auction;
import com.abo.yaas.domain.Bid;
import com.abo.yaas.domain.Category;
import com.abo.yaas.domain.User;
import com.abo.yaas.domain.YaasPojo;
import com.abo.yaas.webservice.client.AuctionProviderProxy;
import com.abo.yaas.webservice.client.BidProviderProxy;
import com.abo.yaas.webservice.client.EntityProviderProxy;
import com.abo.yaas.webservice.client.UserProviderProxy;
import com.abo.yaas.webservice.constants.URLConstants;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.vaadin.Application;
import com.vaadin.service.ApplicationContext.TransactionListener;

@SuppressWarnings({ "serial", "unchecked", "rawtypes" })
public class ResourceProvider implements TransactionListener, UserChangeListener {
    private final Application application;

    private WebResource resource;

    private HTTPBasicAuthFilter filter;

    private final Map<Class<?>, EntityProviderProxy> providerMap = new HashMap<Class<?>, EntityProviderProxy>();
    
    private static final ThreadLocal<ResourceProvider> instance = new ThreadLocal<ResourceProvider>();

    public static AuctionProviderProxy getAuctionProxy() {
        return (AuctionProviderProxy) instance.get().providerMap.get(Auction.class);
    }

    public static BidProviderProxy getBidProxy() {
        return (BidProviderProxy) instance.get().providerMap.get(Bid.class);
    }

    public static ResourceProvider get() {
        return instance.get();
    }
    
    public static <T extends YaasPojo> EntityProviderProxy<T> getEntityProvider(Class<T> entityType) {
        EntityProviderProxy<T> result = instance.get().providerMap.get(entityType);
        if (result == null) {
            result = new EntityProviderProxy<T>(entityType, instance.get().resource);
            if (result != null) {
                instance.get().providerMap.put(entityType, result);
            }
        }
        return result;
    }

    public UserProviderProxy getUserProxy() {
        return (UserProviderProxy) providerMap.get(User.class);
    }

    public static WebResource getWebResource() {
        return instance.get().resource;
    }

    public static void initialize(final Application app) {
        System.out.println("Init");
        if (app == null) {
            throw new IllegalArgumentException("Application may not be null");
        }

        final ResourceProvider handler = new ResourceProvider(app);
        handler.providerMap.put(Auction.class, new AuctionProviderProxy(handler.resource));
        handler.providerMap.put(Bid.class, new BidProviderProxy(handler.resource));
        handler.providerMap.put(User.class, new UserProviderProxy(handler.resource));

        app.getContext().addTransactionListener(handler);
        SessionHandler.addListener(handler);
        instance.set(handler);
    }

    public ResourceProvider(final Application app) {
        application = app;
        buildResource();
    }

    private void buildResource() {
        try {
            final ClientConfig config = new DefaultClientConfig();
            config.getProperties().put(User.class.getSimpleName(), URLConstants.userProvider);
            config.getProperties().put(Auction.class.getSimpleName(), URLConstants.auctionProvider);
            config.getProperties().put(Bid.class.getSimpleName(), URLConstants.bidProvider);
            config.getProperties().put(Category.class.getSimpleName(), URLConstants.categoryProvider);
            config.getClasses().add(EntityResource.WebServiceExceptionMapper.class);
            final Client client = Client.create(config);
            resource = client.resource(getBaseURI());
        } catch (final Exception e) {
            e.printStackTrace();
        }
    }

    private URI getBaseURI() throws IllegalArgumentException, UriBuilderException, URISyntaxException {
        return UriBuilder.fromUri(application.getURL().toURI()).build();
    }

    @Override
    public void onLogin(UserChangeEvent event) {
        if (instance.get().filter != null) {
            instance.get().resource.removeFilter(filter);
        }
        final User newUser = event.getUser();
        instance.get().filter = new HTTPBasicAuthFilter(newUser.getLogin(), newUser.getPassword());
        instance.get().resource.addFilter(instance.get().filter);
    }

    @Override
    public void onLogout(UserChangeEvent event) {
        if (instance.get().filter != null) {
            instance.get().resource.removeFilter(filter);
        }
    }

    @Override
    public void transactionEnd(Application application, Object transactionData) {
        if (this.application == application) {
            instance.set(null);
        }
    }

    @Override
    public void transactionStart(Application application, Object transactionData) {
        if (this.application == application) {
            instance.set(this);
        }
    }
}
